#include "xVMRelationStruct.h"
#include "ReferenceVM.h"
#include "MappingEngine.h"
#include "DIVA_Manager.h"
#include "xNodeType_VM.h"
#include "xNodeType_WOP.h"
#include "xInMappingPort.h"
#include "xOutMappingPort.h"
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <algorithm>
#include <iterator>
#include <sstream>

using namespace std ;
xVMRelationStruct * xVMRelationStruct::singleton = NULL ;

xVMRelationStruct::xVMRelationStruct() {
	_all_mapping_nodes = MappingEngine::getInstance()->getAllxMappingNodesPtr();
}

xVMRelationStruct*  xVMRelationStruct::getInstance() {
	if (singleton == NULL) 
		singleton = new xVMRelationStruct();
	return singleton ;
}

//內部函式,將一個字串以一個字元切成一個字串集合
std::vector<std::string> namesSplit(std::string str, std::string delim)
{
	int cutAt;

	std::vector<std::string> results;

	while( (cutAt = str.find_first_of(delim)) != str.npos )
	{
		if(cutAt > 0)
		{
			results.push_back(str.substr(0,cutAt));
		}
		str = str.substr(cutAt+1);
	}
	if(str.length() > 0)
	{
		results.push_back(str);
	}

	return results;
}

//內部函式,比對一個名稱是否存在於一群名稱(以空白隔開)之中，是就回傳true
bool checkNames(string names, string valName){
	vector<string> names_vec;
	vector<string>::iterator itr ;
	Ogre::String name;

	if(names=="")
		return true;

	names_vec = namesSplit(names, " ");
	for (itr = names_vec.begin(); itr != names_vec.end(); itr++) {
		name = (*itr);
		if(name == valName)
			return true;

	}

	return false;

}

DIVA_VM * xVMRelationStruct::VMRS_toCompositeRootVM(DIVA_VM *vm) {
	xMappingNode * current = MappingEngine::getInstance()->xSearchNodeByVM(vm);
	assert(current != NULL);
	set<xMappingNode*> roots = VMRS_toCompositeRoot(current);
	assert(roots.size() == 1);

	set<xMappingNode*>::iterator itr ;
	itr = roots.begin();
	xMappingNode * root = (*itr);
	if( root->getType() == "vm" ) {
		xNodeType_VM * nt = dynamic_cast< xNodeType_VM * >( root->getNodeTypePtr() );
		return nt->getVM();
	} else if( root->getType() == "gate" ) {
		xNodeType_VM * vp = dynamic_cast< xNodeType_VM * >( root->getInPort( 0 )->getLink()->left->getParent()->getNodeTypePtr() );
		return vp->getVM();
	}
	assert( !"invalid node type" );
	return NULL;

}
set<xMappingNode*> xVMRelationStruct::VMRS_toCompositeRoot(xMappingNode *current) {
	xMappingNode *p = current ;
	set<xMappingNode*> roots;
	// boundary condition
	vector<xMappingNode *> parents = p->getParentNodes();
	if (parents.size() == 0) {
		 roots.insert(current);
		 return roots;
	}
	typedef set<xMappingNode*> ParentSet ;
	vector<xMappingNode *>::iterator itr ;
	for (itr = parents.begin(); itr != parents.end(); itr++) {
		xMappingNode *currentdad = (*itr);
		set<xMappingNode *> newroots ;
		set<xMappingNode*> s1 =  VMRS_toCompositeRoot(currentdad);
		set_union(s1.begin(), s1.end(), roots.begin(), roots.end(),
            insert_iterator<ParentSet>(roots,roots.begin()));
	}
	/*
	while (parents.size() > 0) {
		assert(parents.size() == 1) ; // if assert fails in this statement, call ypc
		p = parents[0] ;
		parents = p->getParentNodes();
	} 
	*/
	return roots ;
}

// This is a recursive function
// Given a mapping node, this function return all the children mapping node
vector<xMappingNode *> xVMRelationStruct::getAllChildren(xMappingNode *current) {

	vector<xMappingNode *> ret, kids ;
	vector<xMappingNode *>::iterator itr ;
	vector<xMappingNode *>::iterator kiditr ;
	vector<xMappingNode *> kidret ;

	// ret.push_back(current) ;
	kids = current->getChildNodes();
	if (kids.size() == 0) return ret ;
	for (itr = kids.begin(); itr != kids.end(); itr++) {
		ret.push_back((*itr));
		kidret =  getAllChildren((*itr));
		for (kiditr = kidret.begin(); kiditr != kidret.end(); kiditr++) {
			ret.push_back((*kiditr));
		}
	}
	return ret ;
}	

vector<DIVA_VM *> xVMRelationStruct::VMRS_nextVMsByRef(DIVA_VM *vm,string refName) {

	vector<xMappingNode *> allkids ;
	vector<xMappingNode *>::iterator itr ;
	xMappingNode *p ;
	vector<DIVA_VM *> ret ;

	xMappingNode * current = MappingEngine::getInstance()->xSearchNodeByVM(vm)  ;
	assert(current != NULL);
	allkids = getAllChildren(current);

	for (itr = allkids.begin(); itr != allkids.end(); itr++) {
		p = (*itr);
		if (p->getType() != "vm") continue ;

		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM() ;
		assert(pvm != NULL);
		string str = pvm->get_vm_name();
		if (pvm->getVMType() == "reference" && pvm->get_vm_name() == refName) {
			ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(pvm) ;
			assert (rvm != NULL);
			ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
			// a reference's body VM is a dummyVM, 
			if (dummy->getBodyVM() != NULL) 
			{
				//check 是否有重複
				if ( (find(ret.begin(), ret.end(), dummy->getBodyVM())) == ret.end())
				{
					ret.push_back(dummy->getBodyVM()) ;
				}

			}
		}
	}
	return ret ;
}

/*vector<DIVA_VM *> xVMRelationStruct::VMRS_nextVMsByRefNames(DIVA_VM *vm, string refNames) {

	vector<xMappingNode *> allkids ;
	vector<xMappingNode *>::iterator itr ;
	xMappingNode *p ;
	vector<DIVA_VM *> ret ;

	xMappingNode * current = MappingEngine::getInstance()->xSearchNodeByVM(vm)  ;
	assert(current != NULL);
	allkids = getAllChildren(current);

	for (itr = allkids.begin(); itr != allkids.end(); itr++) {
		p = (*itr);
		if (p->getType() != "vm") continue ;

		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM() ;
		assert(pvm != NULL);
		if (pvm->getVMType() == "reference" && checkNames(refNames, get_wopName(pvm))) {
			ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(pvm) ;
			assert (rvm != NULL);
			ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
			// a reference's body VM is a dummyVM, 
			if (dummy->getBodyVM() != NULL) 
				ret.push_back(dummy->getBodyVM()) ;

		}
	}
	return ret ;
}
*/
map<DIVA_VM *, DIVA_VM *> xVMRelationStruct::VMRS_nextVMPairsByRef(DIVA_VM *vm) {

	map<DIVA_VM *, DIVA_VM *> ret ;
	vector<xMappingNode *> allkids ;
	vector<xMappingNode *>::iterator itr ;
	xMappingNode *p ;

	xMappingNode * current = MappingEngine::getInstance()->xSearchNodeByVM(vm); ;
	assert(current != NULL);
	allkids = getAllChildren(current);

	for (itr = allkids.begin(); itr != allkids.end(); itr++) {
		p = (*itr);
		if (p->getType() != "vm") continue ;

		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM() ;
		assert(pvm != NULL);
		if (pvm->getVMType() == "reference") {
			ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(pvm) ;
			assert (rvm != NULL);
			ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
			// a reference's body VM is a dummyVM, 
			if (dummy->getBodyVM() != NULL) 
				ret[rvm] = dummy->getBodyVM() ;
		}

	}
	return ret ;
}
vector<DIVA_VM *> xVMRelationStruct::VMRS_preVMsByRef(DIVA_VM *vm,string refName) {
	vector<DIVA_VM *> ret ;
	vector<xMappingNode *>::iterator itr ;

	for (itr= _all_mapping_nodes->begin(); itr != _all_mapping_nodes->end(); itr ++ ) {
		xMappingNode *p = (*itr);
		if (p->getType() != "vm") continue ;

		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM() ;
		assert(pvm != NULL);
		if (pvm->getVMType() == "reference") {
			ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(pvm) ;
			assert (rvm != NULL);
			ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
			DIVA_VM *xx = dummy->getBodyVM();
			if (xx == vm) 
				ret.push_back(rvm);
		}

	}
	return ret ;
}

/*vector<DIVA_VM *> xVMRelationStruct::VMRS_preVMsByRefNames(DIVA_VM *vm, string refNames) {
	vector<DIVA_VM *> ret ;
	vector<xMappingNode *>::iterator itr ;

	for (itr= _all_mapping_nodes->begin(); itr != _all_mapping_nodes->end(); itr ++ ) {
		xMappingNode *p = (*itr);
		if (p->getType() != "vm") continue ;

		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM() ;
		assert(pvm != NULL);
		if (pvm->getVMType() == "reference" && checkNames(refNames, get_wopName(pvm))){
			ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(pvm) ;
			assert (rvm != NULL);
			ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
			DIVA_VM *xx = dummy->getBodyVM();
			if (xx == vm) 
				ret.push_back(rvm);
		}

	}
	return ret ;
}
*/


vector<DIVA_VM *> xVMRelationStruct::get_subVMs(DIVA_VM *vm) {

	vm = VMRS_toCompositeRootVM(vm);
	if(vm->getVMType()=="reference")
	{
		//如果是ref找出他指向的vm
		ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(vm) ;
		assert (rvm != NULL);
		rvm->VMPicked(0,0,0);
		ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
		// a reference's body VM is a dummyVM, 
		if (dummy->getBodyVM() != NULL) 
			vm = dummy->getBodyVM();
	}

	vector<DIVA_VM *> ret ;

	vector<xMappingNode *>::iterator itr ;
	vector<xMappingNode *> allkids ;
	xMappingNode *p ;

	xMappingNode * current = MappingEngine::getInstance()->xSearchNodeByVM(vm) ;
	allkids = getAllChildren(current);

	for (itr = allkids.begin(); itr != allkids.end(); itr++) {
		p = (*itr);
		if (p->getType() != "vm") continue ;
		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM();

		ret.push_back(pvm);
	}
	return ret ;
}


DIVA_VM* xVMRelationStruct::VMRS_getNewCreateVM(){
	vector<xMappingNode *>::iterator itr = _all_mapping_nodes->end()-1;
	xMappingNode *p = (*itr);
	if(p->getType() != "vm"){
		return NULL;
	}
	assert(p->getType() == "vm");
	DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM();
	return pvm;
}
vector<DIVA_VM *> xVMRelationStruct::VMRS_getSameTypeVMs(DIVA_VM* vm){

	vector<DIVA_VM *> ret ;
	vector<xMappingNode *>::iterator itr ;

	for(itr = _all_mapping_nodes->begin(); itr != _all_mapping_nodes->end(); itr++){
		xMappingNode *p = (*itr);
		if (p->getType() != "vm") continue ;
		assert(p->getType() == "vm");
		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM();
		if(pvm->getVMType() == vm->getVMType()){
			ret.push_back(pvm);
		}
	}
	return ret;
}


string searchForWOPNames(xMappingNode * node){
	vector<xInMappingPort*> inps;
	string ret = "";
	xOutMappingPort* outp;
	xMappingNode* outNode;
	vector<xInMappingPort*>::iterator itr;
	xInMappingPort* inp;
	string type;

	if(node->getType()=="wop"){
		xNodeType_WOP* nt_wop;
		nt_wop = (xNodeType_WOP*)(node->getNodeTypePtr());
		ret = (nt_wop->getWOP())->getName() + " ";
	}
	else if(node->getType()=="gate"){	
		// a gate has a unique inport and unique outport.
		inps = node->getInports();
		for(itr = inps.begin();itr!=inps.end();itr++){
			inp = (*itr);
			if (inp->getLink() != NULL) {
			   outp = (inp->getLink())->left;
			   outNode = outp->getParent();
			   ret = ret + searchForWOPNames(outNode);
			}
		}
	}
	else{
		//基本上，如果get_wopName傳進的確定是ref_vm，那麼執行到這裡肯定有問題
		//但考慮到get_wopName不一定用於ref_vm，因此不處理，往後可能要針對各種mapping node的連接方式做整理

		//assert(false); 
	}

	return ret;
}

DIVA_VM* xVMRelationStruct::get_pointedVM(ReferenceVM *vm){
	DIVA_VM* ret;
	ret = vm->getBodyVM();
	ret = ((ReferenceVM*)ret)->getBodyVM();
	return ret;
}

bool xVMRelationStruct::isPointedNULL(ReferenceVM *vm){
	DIVA_VM* refp;
	bool ret;

	refp = vm->getBodyVM();
	if(refp->getVMType()!="reference"){
		assert(false);
		return true;
	}
	ret = ((ReferenceVM*)refp)->isPointedNULL();
	return ret;
}

string xVMRelationStruct::get_wopName(DIVA_VM *vm){
	vector<xMappingNode *>::iterator itr ;
	string ret;
	string names;
	ret = "";

	for(itr = _all_mapping_nodes->begin(); itr != _all_mapping_nodes->end(); itr++){
		xMappingNode *p = (*itr);
		if (p->getType() != "vm") continue ;
		assert(p->getType() == "vm");
		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM();
		if(pvm == vm){
			vector<xMappingNode *> nodes;
			nodes = p->getChildNodes();
			vector<xMappingNode *>::iterator itr2 ;

			for(itr2 = nodes.begin(); itr2 != nodes.end(); itr2++){
				xMappingNode *q = (*itr2);
				/*這裡沒有考慮到varName有被wop關聯到 , 所以name會寫兩次
				  如果原本要查left , 第一次時會先查到varName = left ,
				  此時ret = left , 找到wop後 , wop的name = left
				  所以ret = left left, 找出來是錯誤的name. 
				  我是傾向於有找到name就停止 , 一個vm對應一個wop , wop name不會重複
				  所以要找就找wop的name就好 , 其他port的就不用管
				  如果vm之前有被math node之類的連到就可能有不同的wop name , How to solve ?*/
				names = searchForWOPNames(q);
				ret = ret + names;//?		
			}
		}
	}
	assert(ret.size()>0);
	int s_size = ret.size();
	ret = ret.substr(0,s_size-1);

	return ret;
}

//FORTEST
vector<DIVA_VM*> xVMRelationStruct::VMRS_getAllVM()
{
	vector<xMappingNode*>* xMN = MappingEngine::getInstance()->getAllxMappingNodesPtr();
	vector<DIVA_VM*> VM;

	for(int i=0;i<xMN->size();++i)
	{
		if (xMN->at(i)->getType() == "vm")
		{
			DIVA_VM* rVM = ((xNodeType_VM*)xMN->at(i)->getNodeTypePtr())->getVM();
			//DIVA_VM* rVM = VMRS_toCompositeRootVM(tVM);
			if ( (find(VM.begin(), VM.end(), rVM)) == VM.end())
			{
				rVM->get_vm_name();
				VM.push_back(rVM);
			}
		}
	}
	return VM;
}

vector<DIVA_VM*> xVMRelationStruct::VMRS_getAllVMContainer()
{
	vector<xMappingNode*>* xMN = MappingEngine::getInstance()->getAllxMappingNodesPtr();
	vector<DIVA_VM*> VM;

	for(int i=0;i<xMN->size();++i)
	{
		if (xMN->at(i)->getType() == "vm")
		{
			DIVA_VM* tVM = ((xNodeType_VM*)xMN->at(i)->getNodeTypePtr())->getVM();
			DIVA_VM* rVM = VMRS_toCompositeRootVM(tVM);
			if ( (find(VM.begin(), VM.end(), rVM)) == VM.end())
			{
				rVM->get_vm_name();
				VM.push_back(rVM);
			}
		}
	}
	return VM;
}

vector<DIVA_VM*> xVMRelationStruct::VMRS_searchVMByVarname(string varname)
{
	vector<DIVA_VM*> ret;
	vector<xMappingNode*>* xMN = MappingEngine::getInstance()->getAllxMappingNodesPtr();
	for(int i=0;i<xMN->size();++i)
	{
		if (xMN->at(i)->getType() == "vm")
		{
			DIVA_VM* tVM = ((xNodeType_VM*)xMN->at(i)->getNodeTypePtr())->getVM();
			
			if(tVM->get_vm_name() == varname)
			{
				if(tVM->getVMType()=="reference")
				{
					//如果是ref找出他指向的vm
					ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(tVM) ;
					assert (rvm != NULL);
					rvm->VMPicked(0,0,0);
					ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
					// a reference's body VM is a dummyVM, 
					if (dummy->getBodyVM() != NULL) 
						ret.push_back(dummy->getBodyVM());
				}
				else if (tVM->getVMType()=="vm")
				{
					//如果是vm找出他的composite root vm
					DIVA_VM* rVM = VMRS_toCompositeRootVM(tVM);
					ret.push_back(rVM);
				}
				else if( tVM->getVMType() == "primitive" ) {
					ret.push_back( tVM );
				}
			}
		}
	}

	return ret;
}

vector<string> xVMRelationStruct::get_subVMsVarname(DIVA_VM *vm)
{
	if(vm->getVMType()=="reference")
	{
		//如果是ref找出他指向的vm
		ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(vm) ;
		assert (rvm != NULL);
		rvm->VMPicked(0,0,0);
		ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
		// a reference's body VM is a dummyVM, 
		if (dummy->getBodyVM() != NULL) 
			vm = dummy->getBodyVM();
	}
	vector<DIVA_VM*> vms;
	vector<string> ret;
	vms = get_subVMs(vm);
	string vmName;
	for (int i=0;i<vms.size();++i)
	{
		vmName = vms.at(i)->get_vm_name();
		if ( (find(ret.begin(), ret.end(), vmName)) == ret.end())
		{
			ret.push_back(vmName);
		}
	}
	return ret;
}

vector<string> xVMRelationStruct::get_subVMsVarname(DIVA_VM *vm,string type)
{
	if(vm->getVMType()=="reference")
	{
		//如果是ref找出他指向的vm
		ReferenceVM * rvm = dynamic_cast<ReferenceVM *>(vm) ;
		assert (rvm != NULL);
		rvm->VMPicked(0,0,0);
		ReferenceVM* dummy = dynamic_cast<ReferenceVM *>(rvm->getBodyVM());
		// a reference's body VM is a dummyVM, 
		if (dummy->getBodyVM() != NULL) 
			vm = dummy->getBodyVM();
	}
	vm = VMRS_toCompositeRootVM(vm);
	vector<DIVA_VM*> vms;
	vector<string> ret;
	vms = get_subVMs(vm);
	string vmName;
	for (int i=0;i<vms.size();++i)
	{
		if(vms.at(i)->getVMType()!= type)
			continue;
		vmName = vms.at(i)->get_vm_name();
		if ( (find(ret.begin(), ret.end(), vmName)) == ret.end())
		{
			ret.push_back(vmName);
		}
	}
	return ret;
}

vector<string> xVMRelationStruct::get_subVMsVarname(string str)
{
	vector<string> ret ;
	vector<DIVA_VM*> tvm = VMRS_searchVMByVarname(str);
	if(tvm.size()>0)
	{
		ret = get_subVMsVarname(tvm.at(0));		//暫時都先取用第一個，如有多個再考慮
	}
	return ret;
}

vector<string> xVMRelationStruct::get_subVMsVarname(string str,string type)
{
	vector<string> ret ;
	vector<DIVA_VM*> tvm = VMRS_searchVMByVarname(str);
	if(tvm.size()>0)
	{
		ret = get_subVMsVarname(tvm.at(0),type);		//暫時都先取用第一個，如有多個再考慮
	}
	return ret;
}

vector<DIVA_VM*> xVMRelationStruct::VMRS_getSameTypeVM(DIVA_VM* vm)
{
	/************************************************************************/
	/* not yet                                                                     */
	/************************************************************************/
	vector<DIVA_VM*> ret;
	vector<xMappingNode *>::iterator itr ;
	ret.push_back(vm);
	for(itr = _all_mapping_nodes->begin(); itr != _all_mapping_nodes->end(); itr++){
		xMappingNode *p = (*itr);
		if (p->getType() != "vm") continue ;
		assert(p->getType() == "vm");
		DIVA_VM * pvm = ((xNodeType_VM*)p->getNodeTypePtr())->getVM();
		if(VMRS_checkSameVMType(VMRS_toCompositeRootVM(pvm),vm)){
			vector<DIVA_VM*>::iterator it = 
				find(ret.begin(), ret.end(), VMRS_toCompositeRootVM(pvm));
			if(it == ret.end())
				ret.push_back(VMRS_toCompositeRootVM(pvm));
		}
	}
	return ret;
}

bool xVMRelationStruct::VMRS_checkSameVMType(DIVA_VM* vm1, DIVA_VM* vm2)
{
	vector<DIVA_VM*> ret;
	vector<xMappingNode*>* xMN = MappingEngine::getInstance()->getAllxMappingNodesPtr();
	xMappingNode* xm1 = NULL;
	xMappingNode* xm2 = NULL;
	for(int i=0;i<xMN->size();++i)
	{
		if (xMN->at(i)->getType() == "reference")
		{
			DIVA_VM* tVM = ((xNodeType_VM*)xMN->at(i)->getNodeTypePtr())->getVM();
			if(tVM == vm1)
				xm1 = xMN->at(i);
			else if(tVM == vm2)
				xm2 = xMN->at(i);
		}
	}
	if (xm1==NULL || xm2==NULL)
		return false;
	string st1 = xm1->toMDString(xNodeType::NormalMDSFormat);
	string st2 = xm2->toMDString(xNodeType::NormalMDSFormat);
	vector<string> xms1;
	vector<string> xms2;
	//by MDS formate
	boost::split( xms1, st1, boost::is_any_of( " " ) );
	boost::split( xms2, st2, boost::is_any_of( " " ) );
	if((xms1.at(3) == xms2.at(3))&&(xms1.at(4) == xms2.at(4)))
		return true;
	else
		return false;
}